//
//  ronchigramAppDelegate.m
//  ronchigram
//
//  Created by James LeBeau on 5/21/10.
//  Copyright 2010 subangstrom. All rights reserved.
//

#import "ronchigramAppDelegate.h"

@implementation ronchigramAppDelegate

@synthesize window;

- (id) init
{
	self = [super init];
	if (self != nil) {
		nc = [NSNotificationCenter defaultCenter];
		
		[nc addObserver:self selector:@selector(viewNeedsUpdate:) name:kRonchigramNeedsUpdate object:nil];
		[nc addObserver:self selector:@selector(changeAberration:) name:NSTableViewSelectionIsChangingNotification object: nil];
		
		colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
		
		
	}
	return self;
}


- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
	
	// Insert code here to initialize your application 
	
	//probe = [[SAProbe alloc] init];
	[window setContentAspectRatio:NSMakeSize(956, 673)];
	pixels = NSMakeSize(1024,1024);
	realSize = NSMakeSize(296, 296);	
	
	isAmorphous = YES;
	
	[self prepareProbe];
	[self preparePotential];
	
	
	[viewSize setFloatValue:realSize.width];
	
	prbPotPrd = [[SAComplexMatrix alloc] initWithRows:pixels.height Columns:pixels.width];
	ronchigram = [[SAComplexMatrix alloc] initWithRows:pixels.height Columns:pixels.width];
	
	fftController = [[SAFFTController alloc] initWithInput: prbPotPrd Output: ronchigram];

	

	
	textCellOpts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], 
					NSAllowsEditingMultipleValuesSelectionBindingOption,
					[NSNumber numberWithBool:YES],
					NSCreatesSortDescriptorBindingOption,
					[NSNumber numberWithBool:YES],
					NSConditionallySetsEditableBindingOption,
					[NSNumber numberWithBool:YES],
					NSConditionallySetsEnabledBindingOption,
					[NSNumber numberWithBool:YES],
					NSContinuouslyUpdatesValueBindingOption,
					[NSNumber numberWithBool:YES],
					NSRaisesForNotApplicableKeysBindingOption, nil];

	[textCellOpts retain];
	
	NSTableColumn *columnA = [aberrationTable tableColumnWithIdentifier:@"aCoeff"];
	NSTableColumn *columnB = [aberrationTable tableColumnWithIdentifier:@"bCoeff"];
	
	[columnA bind:@"value" toObject:abController withKeyPath:@"arrangedObjects.Cnma" options:textCellOpts];
	[columnB bind:@"value" toObject:abController withKeyPath:@"arrangedObjects.Cnmb" options:textCellOpts];
	
	
	[self viewNeedsUpdate:nil];
	
}

/* TODO: Create two-resoltion calculations
 
 - low resolution realtime
 - high resolution on mouse up
 
 Calculate potential for high resolution and do bilinear/bicubic interpolation

 Calculate two probes
	- high resolution
	- low resolution

 */
 
- (void) calculateRonchigram{
	
	SAComplexMatrix *wavefunction = [probe wavefunction];
	
	[wavefunction retain];
	
	[wavefunction elementMultiplyWith:[potential potential] Result: prbPotPrd];
	
	[fftController forwardTransform];
	[fftController fftShift:ronchigram];
	
	[wavefunction release];
	
}

- (void) prepareProbe{

	SAAberration *defocus = [SAAberration aberrationWithN:1 M:0 Cnma:-600 Cnmb:0];
	SAAberration *cs =  [SAAberration aberrationWithN:3 M:0 Cnma:1.2E7 Cnmb:0];
	SAAberration *twoFold = [SAAberration aberrationWithN:1 M:2 Cnma:0 Cnmb:0];
	SAAberration *coma = [SAAberration aberrationWithN:2 M:1 Cnma:0 Cnmb:0];
	SAAberration *threeFold = [SAAberration aberrationWithN:2 M:3 Cnma:0 Cnmb:0];

	[defocus setMin:-10000 Max:10000];
	defocus.label = @"Defocus";
	
	[cs setMin:0 Max:3E7];
	cs.symRange = NO;
	cs.label = @"Spherical";
	
	[twoFold setMin:-2000 Max:2000];
	twoFold.label = @"2 fold astigmatism";
	
	[threeFold setMin:-5000 Max:5000];

	threeFold.label = @"3 fold astigmatism";

	[coma setMin:-500000 Max:500000];
	coma.label = @"Coma";
	
	[abController addObject:defocus];
	[abController addObject:cs];
	[abController addObject:twoFold];
	[abController addObject:threeFold];
	[abController addObject:coma];
	
	[defocus release];
	[cs release];
	[twoFold release];
	
	// Setup the probe with some initial general parameters for a standard microscope
	
	NSArray *abs = [abController arrangedObjects];
	
	probe = [SAProbe probeWithAberrations:abs RealSize:realSize.width ApertureSize:10.0 Lambda:.0197 Pixels: pixels];
	
	[abs release];
	[beamEnergy setFloatValue:300];
	[apSize setFloatValue:10.0];
		
	[probe calculateProbe];
}

					   
- (void) preparePotential{
	
	// Setup a potential to form the ronchigram - only needs updating in the case of 
	
	if (potential == nil) {
	potential = [[SAPotential alloc] initWithPixels:pixels RealSize:realSize];
		
	}
	
	if(isAmorphous){
		[potential randomPotentialWithDensity:5 withZ:6];
	}else{
		[potential orderedPotentialWithSpacingA:2 SpacingB:2 Z:6];

	}
	
}

- (IBAction) changeSpecimenType: (id) sender{
	
	
	
}

#pragma mark Image view setup
#pragma mark -

- (CGImageRef) CGImageRefWithMatrix:(SAMatrix*) matrixToImage{
	
	[matrixToImage retain];
	
	int arraySize = [matrixToImage arraySize];
	
	double maxValue = [matrixToImage maxValue];
	double minValue = [matrixToImage minValue];
	
	int numRows = [matrixToImage numRows];
	int numColumns = [matrixToImage numColumns];
	
	int i;
	
	short *matrix = (short*) calloc(numRows*numColumns, sizeof(short));

	for(i = 0; i < arraySize; i++){
		matrix[i] = (short)(([matrixToImage valueAtArrayIndex:i] - minValue)/(maxValue - minValue)*65535.0);
	}
	
	size_t numBytes = sizeof(short)*arraySize;
	
	CGDataProviderRef theProvider = CGDataProviderCreateWithData(NULL,  matrix, numBytes, NULL);
	CGImageRef matrixImage = CGImageCreate([matrixToImage numColumns], 
							[matrixToImage numRows], 
							16, 
							16, 
							sizeof(short)*[matrixToImage numColumns], 
							colorSpace, 
							kCGBitmapByteOrder16Host,
							theProvider, 
							NULL,
							true, 
							kCGRenderingIntentDefault);
	
	
	[matrixToImage release];
	
	free(matrix);
	
	CGDataProviderRelease(theProvider);
	
	return matrixImage;
}

- (void) reloadViewWithMatrix: (SAMatrix*) matrixToView{
	
	[CATransaction begin];
	[CATransaction setDisableActions:YES];
	
	CGImageRef matrixImage  = [self CGImageRefWithMatrix:matrixToView];
	
	
	[saView setImageWithCGImageRef:matrixImage];

	[CATransaction commit];	
	
	CGImageRelease(matrixImage);
	
}


- (void) viewNeedsUpdate:(id) sender{
	
	NSInteger selected = [viewSelectSeg selectedSegment];
	SAMatrix *newViewMatrix;
	
	SAComplexMatrix *conjMatrix = [[SAComplexMatrix alloc] initWithSameSizeAs:ronchigram];
	SAComplexMatrix *newComplexMatrix = [[SAComplexMatrix alloc] initWithSameSizeAs:ronchigram];
	SAComplexMatrix *wave = [probe wavefunction];
	SAComplexMatrix *ap = [probe aperture];

	switch (selected) {
		case 0:

			[self calculateRonchigram];

			// Determine conjugate
			[ronchigram conjugateToResult:conjMatrix];
			// Intensity distribution 
			[conjMatrix elementMultiplyWith:ronchigram Result:newComplexMatrix];
			newViewMatrix = [newComplexMatrix realPart];
			
			break;

		case 1:

			[fftController fftShift:wave];
			[wave conjugateToResult:conjMatrix];
			[conjMatrix elementMultiplyWith:wave Result:newComplexMatrix];
			[fftController fftShift:wave];
			
			newViewMatrix = [newComplexMatrix realPart];
			
			break;
		case 2:
			
			[fftController fftShift:ap];			
			newViewMatrix = [ap realPart];
			[fftController fftShift:ap];
				
		break;

	}
	
	[self reloadViewWithMatrix:newViewMatrix];
	
	[newComplexMatrix release];
	[conjMatrix release];
	[newViewMatrix release];
	
	
}

- (IBAction) resetAberration:(id) sender{
	
	NSArray *selected = [abController selectedObjects];
	
	
	NSUInteger i, count = [selected count];
	
	for (i = 0; i < count; i++) {
		SAAberration * ab = [selected objectAtIndex:i];
		ab.Cnma=0;
		ab.Cnmb=0;
	}
	[probe setAberrations:[abController arrangedObjects]];
	[probe calculateProbe];
	[self viewNeedsUpdate:nil];
	
}

- (IBAction) changeAbLabel: (id) sender{
	
	NSArray *selected = [abController selectedObjects];
	
	
	NSUInteger i, count = [selected count];
	
	for (i = 0; i < count; i++) {
		SAAberration * ab = [selected objectAtIndex:i];
		ab.label= [sender stringValue];
	}

	[probe setAberrations:[abController arrangedObjects]];	
}


- (IBAction) changeAberration:(id) sender{
	
	NSInteger selected;
	
	if([sender isKindOfClass:[NSSegmentedControl class]]){
		
		selected = [sender selectedSegment];
		
		switch (selected) {
			case 0:
				[abController add:nil];
				[nc postNotification:[NSNotification notificationWithName:kRonchigramNeedsUpdate object: nil]];

				break;
			case 1:
				[abController remove:nil];
				[nc postNotification:[NSNotification notificationWithName:kRonchigramNeedsUpdate object: nil]];

				break;
		}
		
	}

	[probe calculateProbe];

	[self viewNeedsUpdate:nil];

	
}

// Annoying but necessary to get the textfields to update the view after changing values

- (BOOL) control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor{
	
	[self changeAberration:nil];
	
	return YES;
	
}



- (IBAction) changeAberrationRange: (id) sender{
	
	SAAberration *selectedAb = [[abController selectedObjects] objectAtIndex:0];
	
	if([selectedAb symRange] == YES){
		
		if ([sender tag] ==0) {
			selectedAb.max= [sender floatValue] * (-1);

		}else{
			selectedAb.min = [sender floatValue]* (-1);

		}
		
	}
}

- (IBAction) changeWavelength:(id) sender{
	
	double energy;
	double length;
	
	energy = [sender doubleValue]*1000;
	
	
	length = 12.2639 / sqrt(energy + 0.97845e-6*energy*energy);;
	
	
	[probe setLambda: length];
	[probe calculateProbe];
	[self viewNeedsUpdate:nil];
	
}


- (IBAction) changeApertureSize: (id) sender{
	
	[probe setApertureSize:[sender floatValue]];
	[probe calculateProbe];
	[nc postNotification:[NSNotification notificationWithName:kRonchigramNeedsUpdate object: nil]];

	
}

- (IBAction) changeRealViewSize: (id) sender{
	
	[probe setRealSize:[sender doubleValue]];
	[probe calculateProbe];
	
	[potential setRealSize: NSMakeSize([sender floatValue], [sender floatValue])];
	
	[self preparePotential];

	[nc postNotification:[NSNotification notificationWithName:kRonchigramNeedsUpdate object: nil]];

}

- (IBAction) changePotentialType: (id) sender{
	
	if([sender class] == [NSPopUpButton class]){
		
		if([sender indexOfSelectedItem]==0){
			isAmorphous = YES;
		}else {
			isAmorphous = NO;
		}
		[self preparePotential];
		[nc postNotification:[NSNotification notificationWithName:kRonchigramNeedsUpdate object: nil]];
	}
	
}


- (void)windowWillClose:(NSNotification *)aNotification {
	[NSApp terminate:self];
}


@end
